﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright company="Collaboris Ltd." file="SafContext.cs">
//   Copyright (c) Collaboris Ltd. All rights Reserved.
// </copyright>
// <summary>
//   Context Used to transmit informatin between Actions in the macro.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Collaboris.Saf.Engine
{
    using System;
    using System.Xml.Serialization;

    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Administration;
    using Microsoft.SharePoint.Utilities;

    using Utils;

    /// <summary>
    /// Context Used to transmit informatin between Actions in the macro.
    /// </summary>
    [Serializable]
    public class SafContext : ContextBase
    {
        #region Constants

        /// <summary>
        /// current web key name.
        /// </summary>
        public const string CurrentWebKeyName = "SpWeb";

        /// <summary>
        /// current site key name.
        /// </summary>
        public const string CurrentSiteKeyName = "SPSite";

        /// <summary>
        /// current web app key name.
        /// </summary>
        public const string CurrentWebAppKeyName = "SpWebApp";

        /// <summary>
        /// current farm key name.
        /// </summary>
        public const string CurrentFarmKeyName = "SPFarm";

        /// <summary>
        /// current scope key name.
        /// </summary>
        public const string CurrentScopeKeyName = "CurrentScope";

        private SPWeb web;

        private SPSite site;

        private SPWebApplication webApplication;

        private SPFarm farm;

        private SPScope scope;
        
        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets CurrentScope.
        /// </summary>
        public SPScope CurrentScope
        {
            get { return scope; }

            set { scope = value; }
        }

        /// <summary>
        /// Gets the URL
        /// </summary>
        /// <value>The URL for the Web.</value>
        [XmlAttribute]
        public string Url
        {
            get
            {
                if (this.Web != null)
                {
                    if (this.Web.Exists)
                        return this.Web.Url;
                }

                return string.Empty;
            }
        }

        /// <summary>
        /// Gets or sets Web.
        /// </summary>
        [XmlIgnore]
        public SPWeb Web
        {
            get { return web; }

            set { web = value; }
        }

        /// <summary>
        /// Gets or sets Site.
        /// </summary>
        [XmlIgnore]
        public SPSite Site
        {
            get
            {
                return site;
            }

            set
            {
                site = value;
            }
        }

        /// <summary>
        /// Gets or sets Farm.
        /// </summary>
        [XmlIgnore]
        public SPFarm Farm
        {
            get { return farm; }

            set { farm = value; }
        }

        /// <summary>
        /// Gets or sets WebApp.
        /// </summary>
        [XmlIgnore]
        public SPWebApplication WebApp
        {
            get { return webApplication; }

            set { webApplication = value; }
        }

        #endregion

        #region Factory Methods

        /// <summary>
        /// Creates the context.
        /// </summary>
        /// <returns>
        /// </returns>
        public static SafContext CreateContext()
        {
            return new SafContext();
        }

        /// <summary>
        /// Creates the context.
        /// </summary>
        /// <param name="web">
        /// The web.
        /// </param>
        /// <param name="scope">
        /// The scope.
        /// </param>
        /// <returns>
        /// </returns>
        public static SafContext CreateContext(SPWeb web, SPScope scope)
        {
            ValidationHelper.VerifyObjectArgument(web, "web");
            ValidationHelper.VerifyObjectArgument(scope, "scope");

            return new SafContext(web, scope);
        }

        /// <summary>
        /// create context.
        /// </summary>
        /// <param name="site">
        /// 
        /// Reference to the SPSite that will be used to run the SafContext. Pass null if not required
        /// </param>
        /// <param name="scope">
        /// The scope.
        /// </param>
        /// <returns>
        /// </returns>
        public static SafContext CreateContext(SPSite site, SPScope scope)
        {
            ValidationHelper.VerifyObjectArgument(site, "site");
            ValidationHelper.VerifyObjectArgument(scope, "scope");

            return new SafContext(site, scope);
        }

        /// <summary>
        /// create context.
        /// </summary>
        /// <param name="webApplication">
        /// The web application.
        /// </param>
        /// <param name="scope">
        /// The scope.
        /// </param>
        /// <returns>
        /// </returns>
        public static SafContext CreateContext(SPWebApplication webApplication, SPScope scope)
        {
            ValidationHelper.VerifyObjectArgument(webApplication, "webApplication");
            ValidationHelper.VerifyObjectArgument(scope, "scope");

            return new SafContext(webApplication, scope);
        }


        /// <summary>
        /// create context.
        /// </summary>
        /// <param name="farm">
        /// The farm.
        /// </param>
        /// <param name="scope">
        /// The scope.
        /// </param>
        /// <returns>
        /// </returns>
        public static SafContext CreateContext(SPFarm farm, SPScope scope)
        {
            ValidationHelper.VerifyObjectArgument(farm, "farm");
            ValidationHelper.VerifyObjectArgument(scope, "scope");

            return new SafContext(farm, scope);
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Prevents a default instance of the <see cref="SafContext"/> class from being created. 
        /// Initializes a new instance of the SafContext class.
        /// </summary>
        private SafContext()
        {
            
        }

        /// <summary>
        /// Initializes a new instance of the SafContext class.
        /// </summary>
        /// <param name="web">
        /// The web.
        /// </param>
        /// <param name="scope">
        /// The scope.
        /// </param>
        private SafContext(SPWeb web, SPScope scope)
            : base()
        {
            this.Trace.TraceMethodStart("SafContext(SPWeb)");

            this.Trace.TraceVerbose("Creating new SafContext for this web '{0}', for this scope '{1}'", web.Url, scope);
            
            this.CurrentScope = scope;
            
            // this.AddPlaceHolders();

            this.SetRefsForWeb(web);
            this.CurrentScope = SPScope.Web;

            this.Trace.TraceMethodEnd("SafContext(SPWeb)");
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SafContext"/> class. 
        /// </summary>
        /// <param name="site">
        /// Reference to the SPSite that will be used to run the SafContext. Pass null if not required
        /// </param>
        /// <param name="scope">
        /// The scope.
        /// </param>
        private SafContext(SPSite site, SPScope scope) 
            : base()
        {
            this.CurrentScope = scope;

            this.Trace.TraceMethodStart("SafContext(SPSite)");

            // this.AddPlaceHolders();

            this.SetRefsForSite(site);
            this.CurrentScope = SPScope.Site;

            this.Trace.TraceMethodEnd("SafContext(SPSite)");
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SafContext"/> class. 
        /// Creates a new instance of an SafContext
        /// </summary>
        /// <param name="webApplication">
        /// Reference to the SPWebApplication that will be used to 
        /// run the SafContext. Pass null if not required
        /// </param>
        /// <param name="scope">
        /// The scope.
        /// </param>
        private SafContext(SPWebApplication webApplication, SPScope scope)
            : base()
        {
            // Trace = new TraceHelper(this);
            this.CurrentScope = scope;

            this.Trace.TraceMethodStart("SafContext(SPWebApplication)");

            this.SetRefsForWebApplication(webApplication);
            this.CurrentScope = SPScope.WebApplication;

            this.Trace.TraceMethodEnd("SafContext(SPWebApplication)");
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SafContext"/> class. 
        /// Creates a new instance of an SafContext
        /// </summary>
        /// <param name="farm">
        /// Reference to the SPFarm that 
        /// will be used to run the SafContext. 
        /// Pass null if not required
        /// </param>
        /// <param name="scope">
        /// The scope.
        /// </param>
        private SafContext(SPFarm farm, SPScope scope) 
            : base()
        {
            
            this.CurrentScope = scope;

            this.Trace.TraceMethodStart("SafContext(SPFarm)");

            //this.AddPlaceHolders();

            this.SetRefsForFarm(farm);
            this.CurrentScope = SPScope.Farm;

            this.Trace.TraceMethodEnd("SafContext(SPFarm)");
        }

        #endregion

        #region Helper Methods

        /// <summary>
        /// Adds the default variables to the SafContext. The following default variables will be added:
        /// For site scoped feature: 'SPSite.Url' and 'SPSite.ID'
        /// For web scoped features: 'SPWeb.Url' and 'SPWeb.ID'
        /// For farm scoped features: 'SPFarm.ID'
        /// For web application scoped features: 'SPWebApp.ID'
        /// </summary>
        private void AddDefaultVariables()
        {
            this.Trace.TraceMethodStart("AddDefaultVariables");

            if (this.Web != null)
            {
                this.ContextProperties.Add("CurrentWeb.Url", this.Web.Url, false);
                this.ContextProperties.Add("CurrentWeb.ID", this.Web.ID.ToString(), false);
                this.ContextProperties.Add("CurrentWeb.Name", this.Web.Name, false);
                // TODO: Do we dispose ?
                if (this.Web.ParentWeb != null)
                {
                    SPWeb parentWeb = this.Web.ParentWeb;
                    this.ContextProperties.Add("ParentWeb.Url", parentWeb.Url, false);
                    this.ContextProperties.Add("ParentWeb.ID", parentWeb.ID.ToString(), false);
                    this.ContextProperties.Add("ParentWeb.Name", parentWeb.Name, false);
                }
            }

            if (this.Site != null)
            {
                this.ContextProperties.Add("CurrentSite.Url", this.Site.Url, false);
                this.ContextProperties.Add("CurrentSite.ID", this.Site.ID.ToString(), false);
            }

            if (this.Farm != null)
            {
                this.ContextProperties.Add("CurrentFarm.ID", this.Farm.Id.ToString(), false);
            }

            if (this.WebApp != null)
            {
                this.ContextProperties.Add("CurrentWebApp.ID", this.WebApp.Id.ToString(), false);

                Uri responseUri = this.WebApp.GetResponseUri(SPUrlZone.Default);
                if (responseUri != null)
                    this.ContextProperties.Add("CurrentWebApp.DefaultUrl", responseUri.AbsoluteUri);
            }

            // add the 12 Hive Paths
            this.ContextProperties.Add("TwelveHive.RootDir", SPUtility.GetGenericSetupPath(String.Empty), false);
            
            this.Trace.TraceMethodEnd("AddDefaultVariables");
        }

        /// <summary>
        /// Sets the refs.
        /// </summary>
        /// <param name="web">The web.</param>
        private void SetRefsForWeb(SPWeb web)
        {
            this.Trace.TraceMethodStart("SafContext.SetRefsForWeb");
            this.Web = web;
            if (this.Web == null)
            {
                throw new NullReferenceException("Cannot get a reference to an SPWeb");
            }

            this.SetRefsForSite(web.Site);

            this.Trace.TraceMethodEnd("SafContext.SetRefsForWeb");
        }

       

        ///// <summary>
        ///// Adds the environment variables Action Context.
        ///// </summary>
        //private void AddEnvironmentVariables()
        //{
        //    this.Trace.TraceMethodStart("AddEnvironmentVariables");

        //    Hashtable envVars = (Hashtable)Environment.GetEnvironmentVariables();
        //    foreach (string key in envVars.Keys)
        //    {
        //        string newKey = StringHelper.EscapeForFormat(key);
        //        string newVal = envVars[key].ToString();
        //        newVal = StringHelper.EscapeForFormat(newVal);

        //        this.TryAdd(key, newVal, "Environment", PropertyBagType.Context);
        //    }

        //    AddCurrentUserVariable();

        //    this.Trace.TraceMethodEnd("AddEnvironmentVariables");
        //}

        /// <summary>
        /// Sets the refs for site.
        /// </summary>
        /// <param name="site">
        /// The site.
        /// </param>
        private void SetRefsForSite(SPSite site)
        {
            this.Trace.TraceMethodStart("SafContext.SetRefsForSite");

            this.Site = site;
            if (this.Site == null)
            {
                throw new NullReferenceException("Cannot get a reference to an SPSite");
            }

            this.SetRefsForWebApplication(site.WebApplication);

            this.Trace.TraceMethodEnd("SafContext.SetRefsForSite");
        }

        /// <summary>
        /// Sets the refs for web app.
        /// </summary>
        /// <param name="webApp">
        /// The web app.
        /// </param>
        private void SetRefsForWebApplication(SPWebApplication webApp)
        {
            this.Trace.TraceMethodStart("SafContext.SetRefsForWebApplication");
            this.WebApp = webApp;
            if (this.WebApp == null)
            {
                throw new NullReferenceException("Cannot get a reference to an SPWebApplication");
            }

            this.SetRefsForFarm(webApp.Farm);
            this.Trace.TraceMethodEnd("SafContext.SetRefsForWebApplication");
        }

        /// <summary>
        /// Sets the refs for farm.
        /// </summary>
        /// <param name="farm">
        /// The farm.
        /// </param>
        private void SetRefsForFarm(SPFarm farm)
        {
            this.Trace.TraceMethodStart("SafContext.SetRefsForFarm");
            this.Farm = farm;
            if (this.Farm == null)
            {
                throw new NullReferenceException("Cannot get a reference to an SPFarm");
            }

            this.AddDefaultVariables();

            this.Trace.TraceMethodEnd("SafContext.SetRefsForFarm");
        }

        #endregion
    }
}